package com.xsy.demo.servlet;

import java.io.File;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.servlet.ServletConfig;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.xsy.demo.annotation.Autowired;
import com.xsy.demo.annotation.Controller;
import com.xsy.demo.annotation.RequestMapping;
import com.xsy.demo.annotation.RequestParam;
import com.xsy.demo.annotation.Service;
import com.xsy.demo.controller.EmployeeController;

public class DispatcerServlet extends HttpServlet{
	private final List<String> classPath=new ArrayList<>();
	
	private final Map<String,Object> map=new HashMap<>();

	private final Map<String,Object> urlMap=new HashMap<>();
	
	public DispatcerServlet() throws ClassNotFoundException{
		
	}
	
	private void doScanPackage(String basePackage) throws ClassNotFoundException {
		URL url=this.getClass().getClassLoader().getResource("/"+basePackage.replaceAll("\\.", "/"));
		String fileStr=url.getFile();
		File file=new File(fileStr);
		String[] filesStr=file.list();
		for(String filePath:filesStr){
			File fileP=new File(fileStr+filePath);
			if(fileP.isDirectory()){
				doScanPackage(basePackage+"."+filePath);
			}else{
				classPath.add(basePackage+"."+fileP.getName());
			}
		}
		try {
			doInstance();
		} catch (InstantiationException | IllegalAccessException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
	}

	private void doInstance() throws ClassNotFoundException, InstantiationException, IllegalAccessException {
		if(classPath.size()==0){
			System.out.println("scan failed============");
			return;
		}
		for(String name: classPath){
			String cn=name.replace(".class", "");
			Class<?> clazz=Class.forName(cn);
			if(clazz.isAnnotationPresent(Controller.class)){
				Object obj=clazz.newInstance();
				RequestMapping rm=clazz.getAnnotation(RequestMapping.class);
				String key=rm.value();
				map.put(key, obj);
			}else if(clazz.isAnnotationPresent(Service.class)){
				Object obj=clazz.newInstance();
				Service rm=clazz.getAnnotation(Service.class);
				String key=rm.value();
				map.put(key, obj);
			}else{
				continue;
			}
			
		}
		
	}

	public void init(ServletConfig cfg){
		try {
			doScanPackage("com.xsy");
			for(String name: classPath){
				System.out.println(name);
			}
			try {
				doInstance();
			} catch (InstantiationException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			for (Map.Entry<String, Object> entity:map.entrySet()) {
				System.out.println(entity.getKey()+"="+entity.getValue());
			}
			doIoc();
			buildMapping();
			for (Map.Entry<String, Object> entity:urlMap.entrySet()) {
				System.out.println(entity.getKey()+"="+entity.getValue());
			}
		} catch (ClassNotFoundException | IllegalArgumentException | IllegalAccessException e) {
			e.printStackTrace();
		}
		
	}
	private void buildMapping() {
		if(map.entrySet().size()<=0){
			System.out.println("init bean failed=============");
			return ;
		}
		for (Map.Entry<String, Object> entry: map.entrySet()){
			Object instance=entry.getValue();
			Class<?> clazz=instance.getClass();
			if(clazz.isAnnotationPresent(Controller.class)){
				RequestMapping rem=clazz.getAnnotation(RequestMapping.class);
				String url=rem.value();
				Method[] methods=clazz.getMethods();
				for(Method md:methods){
					if(md.isAnnotationPresent(RequestMapping.class)){
						RequestMapping rp=md.getAnnotation(RequestMapping.class);
						String value=rp.value();
						urlMap.put(url+value, md);
					}else{
						continue;
					}
				}
			}
		}
	}

	private void doIoc() throws IllegalArgumentException, IllegalAccessException {
		if(map.entrySet().size()<=0){
			System.out.println("init bean failed=============");
			return ;
		}
		for (Map.Entry<String, Object> entry: map.entrySet()){
			Object instance=entry.getValue();
			Class<?> clazz=instance.getClass();
			if(clazz.isAnnotationPresent(Controller.class)){
				Field[] fie=clazz.getDeclaredFields();//获取所有方法包括私有的
				for(Field f:fie){
					if(f.isAnnotationPresent(Autowired.class)){
						f.setAccessible(true);//private授权赋值
						f.set(instance, map.get(f.getAnnotation(Autowired.class).value()));
					}
				}
			}else{
				continue;
			}
		}
	}

	protected void  doGet(HttpServletRequest req,HttpServletResponse res) {
		this.doPost(req, res);
	}
	
	protected void  doPost(HttpServletRequest req,HttpServletResponse res) {
		String uri=req.getRequestURI();
		String contextPath=req.getContextPath();
		String path=uri.replace(contextPath, "");
		Method m=(Method) urlMap.get(path);
		String key="/"+path.split("/")[1];
		Object oo=map.get(key);
		EmployeeController con=(EmployeeController)oo ;
		Object[] args=this.hand(req, res, m);
		try {
			m.invoke(con, args);
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			e.printStackTrace();
		}
	}
	private static Object[] hand(HttpServletRequest req,HttpServletResponse res,Method method){
		Class<?>[] paramClazzs=method.getParameterTypes();
		Object[] args=new Object[paramClazzs.length];
		int args_i=0;
		int index=0;
		for(Class<?> paramClazz:paramClazzs){
			if(ServletRequest.class.isAssignableFrom(paramClazz)){
				args[args_i++]=req;
			}
			if(ServletResponse.class.isAssignableFrom(paramClazz)){
				args[args_i++]=res;
			}
			Annotation[] paramAns=method.getParameterAnnotations()[index];
			if(paramAns.length>0){
				for (Annotation paramAn:paramAns) {
					if(RequestParam.class.isAssignableFrom(paramAn.getClass())){
						RequestParam rp=(RequestParam) paramAn;
						args[args_i++]=req.getParameter(rp.value());
					}
				}
			}
			index++;
		}
		return args;
	}
	
}
